

// Template widgets and boiler-plate graphical elements with already-implemented functionality //

#ifndef __QTWI_ITEMPLATES_HPP
#define __QTWI_ITEMPLATES_HPP

#include "iapplication.hpp"
#include "iwidgets.hpp"
#include "core/iifactory.hpp"
#include "common.hpp"

// Templates are also IWidgets
// Every class that implements a Template has to provide the following virtual methods and apply
// all the operations necessary to its members.
class ITemplate : public IWidget
{
private:
	IWidget &parent;
	Ifactory &f;

protected:
	Ifactory& ftory() const
	{
		return f;
	}

	IWidget& prent() const
	{
		return parent;
	}

	virtual void move(int x, int y) { }
	virtual void resize(int w, int h) { }
	virtual void refresh() { }
	virtual void show() { }

public:
	ITemplate(IWidget &p, Ifactory &fa) : parent(p), f(fa) { }
	
};


class ILabeledSlider : public ITemplate, public IActionHandler
{
	
protected:
	
	ILabel *caption;
	IScrollbar *bar;
	ILabel *value;
	
public:
	ILabeledSlider(IWidget &parent, Ifactory &f, const char *cap,
		       int x, int y, int w, int h, int min, int max, int step, int page, bool grown_up);

	void handler(IWidget &)
	{
		std::string cur = _qtwi_to_string<int>(bar->current());
		value->setText(cur);
	}

	int current() const
	{
		return bar->current();
	}

	void move(int x, int y)
	{
		caption->move(x, y);
		bar->move(x + caption->getWidth() + 5, y);
		value->move(x + caption->getWidth() + bar->getWidth() + 10, y);
	}
	
	IScrollbar* getBar() const
	{
		return bar;
	}

	ILabel* getLabel() const
	{
		return value;
	}

	void setCurrent(int c)
	{
		std::string cr = _qtwi_to_string<int>(c);
		value->setText(cr);
		bar->setCurrent(c);
	}

	void resize(int w, int)
	{
		// not implemented
	}

	void refresh()
	{
		caption->refresh();
		bar->refresh();
		value->refresh();
	}

	void show()
	{
		caption->show();
		value->show();
		bar->show();
	}
};

class ILabeledTextBox : public ITemplate
{
protected:
	ILabel *caption;
	ITextBox *tb;
	
public:
	ILabeledTextBox(IWidget &parent, Ifactory &f, const char *cap,
			int x, int y, int w, int h) : ITemplate(parent, f)
	{

		const int capt_w = 35, lbl_h = 15, tb_h = 21;

		// todo: texbox greyed out
		
		caption = ftory().makeLabel(&prent(), cap, x, y);
		caption->resize(capt_w, lbl_h);

		tb = ftory().makeTextBox(&prent(), "", x + capt_w + 5, y, ITextBox::Single);
		tb->resize(w - capt_w -5, tb_h);

		caption->show();
		tb->show();
	}

	ITextBox* textbox() const
	{
		return tb;
	}

	void move(int x, int y)
	{
		caption->move(x, y);
		tb->move(x + caption->getWidth() + 5, y);
	}

	void resize(int w, int h)
	{
		caption->resize(0.33 * w, 21);
		tb->resize(0.67 * w, 21);
	}

	void refresh()
	{
		caption->refresh();
		tb->refresh();
	}

	void show()
	{
		caption->show();
		tb->show();
	}
};


// Generic wrapper for widget grouping.
// Thus far implemented are IRawWidget and IRadioGroup as graphical containers.

template <class QtwiWidget>
class IWidgetWrapper
{

#define rtti_wdg_cmp(TYPE) \
	(typeid(pwdg) == typeid(TYPE*))

private:
	Ifactory &f;
	IWidget *wdg;
	QtwiWidget *pwdg;
	
protected:
	
	Ifactory& ftory() const
	{
		return f;
	}
	
public:
	QtwiWidget* widget() const
	{
		return pwdg;
	}
	
	IWidgetWrapper(IWidget &parent, Ifactory &fact, const int &x, const int &y,
		       const int &w, const int &h);

};

template <class QtwiWidget>
IWidgetWrapper<QtwiWidget>::IWidgetWrapper(IWidget &parent, Ifactory &fact, const int &x, const int &y,
					   const int &w, const int &h)
	: f(fact), wdg(NULL)
{
	// RTTI is needed here because factory methods aren't generic for all widgets (could have used dynamic_cast)
		
	if(rtti_wdg_cmp(IRawWidget))
		wdg = f.makeRawWidget(&parent, "", x, y);
	else if(rtti_wdg_cmp(IRadioGroup))
		wdg = f.makeRadioGroup(&parent, "", x, y);

	pwdg = dynamic_cast<QtwiWidget*>(wdg);
		
	wdg->resize(w, h);
}


////////////////
// Monologues //
////////////////

class IMonologue : public IWidget
{
private:
	Ifactory &f;
	IWindow *w;
public:
	static const int standard_height = 100;
	static const int standard_width = 280;
	static const int content_margin = 1;
	
	IMonologue(Ifactory &fact, const char *title, int x, int y, int wid = standard_width, int hei = standard_height) : f(fact)
	{
		w = f.makeWindow(title, x, y);
		w->resize(wid, hei);
		w->show();
	}

protected:

	Ifactory& ftory() const
	{
		return f;
	}
	
	IWindow* window() const
	{
		return w;
	}

	void show()
	{

	}
};

class IYesNoMonologue : public IMonologue
{

};

class IStandardMonologue : public IMonologue, private IActionHandler
{
private:
	ILabel *l;
	IButton *b;
	IRawWidget *r;
public:

	static const int button_width = 55;
	static const int button_height = 20;
	
	IStandardMonologue(Ifactory &f, const char *title, int x, int y, std::string text)
		: IMonologue(f, title, x, y)
	{
		r = ftory().makeRawWidget(window(), "", 0, 0);

		r->resize(IMonologue::standard_width, IMonologue::standard_height);
		
		l = ftory().makeLabel(window(), text.c_str(),
				      IMonologue::content_margin, IMonologue::content_margin);

		b = ftory().makeButton(window(), "OK", 0.5 * (IMonologue::standard_width -
							      IStandardMonologue::button_width),
				       IMonologue::standard_height -
				       IMonologue::content_margin - IStandardMonologue::button_height);

		b->resize(button_width, button_height);
		l->resize(IMonologue::standard_width - 2 * IMonologue::content_margin, IMonologue::standard_height * 0.9);
		
		b->show();
		l->show();

		ftory().getApp().registerAction(*b, IAction::CLICK, this);
	}
	
	virtual void handler(IWidget &)
	{
		window()->hide();
		
		// FIXME: ungeneric; Qt's memory management is somewhat more capable of cleaning up itself ...
#ifdef QTWI_TWN
		delete l;
		delete window();
#endif

	}
};

class IInfoMonoglue : public IMonologue
{
private:
	ITextBox *tb;
	IButton *bu;
public:

	//
	// Todo: text box color grey
	// 
	
	static const int info_monolog_width = 200;
	static const int info_monolog_heigth = 150;

	IInfoMonoglue(Ifactory &f, const char *title, int x, int y,  std::string info) :
		IMonologue(f, title, x, y, info_monolog_width, info_monolog_heigth)
	{ }

	ITextBox* textbox() const
	{
		return tb;
	}		      

};

//
// This QtWi GUI template supports the following features:
//
//    - Widgets can be added to it horizontally, that is next to the lastly added element
//      or a new row can be opened
//    - Those widgets are drawn in order automatically taking into account the width constraint
//    - This widget wrapper grows dynamically in height according to which elements are added to it
//
// So it could be perceived as a somewhat very weak layout.
//
template<class T>
class IDynamicWrapper : public IWidgetWrapper<T>
{
	typedef std::vector<IWidget*> horizont_wdgs;
	typedef std::vector<horizont_wdgs> vertical_wdgs;
	
private:

	vertical_wdgs vwdgs;
	
	int height;
	int width;
	
	static const int widget_buffer = 5;
	static const int assumed_widget_height = 21;
	
public:

	enum AddOrientation {
		
		RIGHTHAND,
		BELOW
	};
	
public:
	
	IDynamicWrapper(IWidget &parent, Ifactory &f, const int &x, const int &y, int w) :
		IWidgetWrapper<T>(parent, f, x, y, w, 20), width(w)
	{
		
	}

	// Note: First widget does always have to be added with orientation BELOW
	void add_widget(IWidget *w, AddOrientation o)
	{
		if(o == RIGHTHAND) {
			vwdgs.back().push_back(w);
		} else {
			horizont_wdgs h;
			h.push_back(w);
			vwdgs.push_back(h);
		}
	}

	void draw(int height_tweak = 0)
	{

		int current_y = widget_buffer + 15;

		for(vertical_wdgs::const_iterator i = vwdgs.begin(); i != vwdgs.end(); i++) {


			horizont_wdgs h = *i;
			int element_width = width / h.size() - h.size() * widget_buffer;
			int current_x = widget_buffer + 10;
			
			for(horizont_wdgs::const_iterator j = h.begin(); j != h.end(); j++) {

				IWidget *now = *j;
				
				now->move(current_x, current_y);
				now->resize(element_width, assumed_widget_height);
				now->refresh();
				now->show();
				
				current_x += element_width + widget_buffer;
			}

			current_y += assumed_widget_height + widget_buffer;
		}
		
		dynamic_height_adjust(height_tweak);
	}

	void dynamic_height_adjust(int height_tweak)
	{

		int overall_height = vwdgs.size() * (assumed_widget_height + widget_buffer + 10);

		height = overall_height + height_tweak;
		
		IWidgetWrapper<T>::widget()->resize(width, height);
	}

	int getHeight() const
	{
		return height;
	}
};


#endif /* __QTWI_ITEMPLATES_HPP */
